home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / shllutil.lha / shellutils-1.8 / src / test.c < prev    next >
C/C++ Source or Header  |  1992-10-28  |  24KB  |  1,055 lines

  1. /* GNU test program (ksb and mjb) */
  2.  
  3. /* Modified to run with the GNU shell by bfox. */
  4.  
  5. /* Copyright (C) 1987, 1988, 1989, 1990, 1991 Free Software Foundation, Inc.
  6.  
  7.    This file is part of GNU Bash, the Bourne Again SHell.
  8.  
  9.    Bash is free software; you can redistribute it and/or modify it under
  10.    the terms of the GNU General Public License as published by the Free
  11.    Software Foundation; either version 2, or (at your option) any later
  12.    version.
  13.  
  14.    Bash is distributed in the hope that it will be useful, but WITHOUT ANY
  15.    WARRANTY; without even the implied warranty of MERCHANTABILITY or
  16.    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  17.    for more details.
  18.  
  19.    You should have received a copy of the GNU General Public License along
  20.    with Bash; see the file COPYING.  If not, write to the Free Software
  21.    Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  22.  
  23. /* Define TEST_STANDALONE to get the /bin/test version.  Otherwise, you get 
  24.    the shell builtin version. */
  25. /* #define TEST_STANDALONE */
  26.  
  27. #include <stdio.h>
  28. #include <sys/types.h>
  29.  
  30. #if !defined (TEST_STANDALONE)
  31. #  include "shell.h"
  32. #  include "posixstat.h"
  33. #  include "filecntl.h"
  34. #else /* TEST_STANDALONE */
  35. #  include "system.h"
  36. #  if !defined (S_IXUGO)
  37. #    define S_IXUGO 0111
  38. #  endif /* S_IXUGO */
  39. #  if defined (_POSIX_VERSION)
  40. #    include <limits.h>
  41. #  else /* !_POSIX_VERSION */
  42. #    include <sys/param.h>
  43. #  endif /* _POSIX_VERSION */
  44. #  if defined (NGROUPS_MAX) || defined (_SC_NGROUPS_MAX) || defined (NGROUPS)
  45. #    define HAVE_GETGROUPS
  46. #  endif /* NGROUPS_MAX || _SC_NGROUPS_MAX || NGROUPS */
  47. #define whitespace(c) (((c) == ' ') || ((c) == '\t'))
  48. #define digit(c)  ((c) >= '0' && (c) <= '9')
  49. #define digit_value(c) ((c) - '0')
  50. char *program_name;
  51. #endif /* TEST_STANDALONE */
  52.  
  53. #if !defined (_POSIX_VERSION)
  54. #  include <sys/file.h>
  55. #endif /* !_POSIX_VERSION */
  56.  
  57. #include <errno.h>
  58. #if !defined (errno)
  59. extern int errno;
  60. #endif /* !errno */
  61.  
  62. #if !defined (STREQ)
  63. #  define STREQ(a, b) ((a)[0] == (b)[0] && strcmp (a, b) == 0)
  64. #endif /* !STREQ */
  65.  
  66. #if !defined (member)
  67. #  define member(c, s) (int)((c) ? index ((s), (c)) : 0)
  68. #endif /* !member */
  69.  
  70. #if defined (_POSIX_VERSION)
  71.  
  72. /* Even though SunOS 4, Ultrix 4, and 386BSD are mostly POSIX.1 compliant,
  73.    their getgroups system call (except in the `System V' environment, which
  74.    is troublesome in other ways) fills in an array of int, not gid_t
  75.    (which is `short' on those systems).  Kludge, kludge.  */
  76.  
  77. #if !defined(sun) && !defined(ultrix) && !defined(__386BSD__)
  78. #define GETGROUPS_T gid_t
  79. #else
  80. #define GETGROUPS_T int
  81. #endif
  82. #else /* !_POSIX_VERSION */
  83. #define GETGROUPS_T int
  84. #endif /* !_POSIX_VERSION */
  85.  
  86. extern gid_t getgid (), getegid ();
  87. extern uid_t geteuid ();
  88.  
  89. #if !defined (R_OK)
  90. #define R_OK 4
  91. #define W_OK 2
  92. #define X_OK 1
  93. #define F_OK 0
  94. #endif /* R_OK */
  95.  
  96. /* The following few defines control the truth and false output of each stage.
  97.    TRUE and FALSE are what we use to compute the final output value.
  98.    SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
  99.    TRUTH_OR is how to do logical or with TRUE and FALSE.
  100.    TRUTH_AND is how to do logical and with TRUE and FALSE..
  101.    Default is TRUE = 1, FALSE = 0, TRUTH_OR = a | b, TRUTH_AND = a & b,
  102.     SHELL_BOOLEAN = (!value). */
  103. #define TRUE 1
  104. #define FALSE 0
  105. #define SHELL_BOOLEAN(value) (!(value))
  106. #define TRUTH_OR(a, b) ((a) | (b))
  107. #define TRUTH_AND(a, b) ((a) & (b))
  108.  
  109. #if defined (TEST_STANDALONE)
  110. #  define test_exit(val) exit (val)
  111. #else
  112.    static jmp_buf test_exit_buf;
  113.    static int test_error_return = 0;
  114. #  define test_exit(val) test_error_return = val, longjmp (test_exit_buf, 1)
  115. #endif /* !TEST_STANDALONE */
  116.  
  117. char *xrealloc ();
  118.  
  119. static int pos;        /* The offset of the current argument in ARGV. */
  120. static int argc;    /* The number of arguments present in ARGV. */
  121. static char **argv;    /* The argument list. */
  122.  
  123. static int unop ();
  124. static int binop ();
  125. static int unary_operator ();
  126. static int binary_operator ();
  127. static int two_arguments ();
  128. static int three_arguments ();
  129. static int posixtest ();
  130.  
  131. static int expr ();
  132. static int term ();
  133. static int and ();
  134. static int or ();
  135.  
  136. static void
  137. test_syntax_error (format, arg)
  138.      char *format, *arg;
  139. {
  140.   fprintf (stderr, "%s: ", argv[0]);
  141.   fprintf (stderr, format, arg);
  142.   fflush (stderr);
  143.   test_exit (SHELL_BOOLEAN (FALSE));
  144. }
  145.  
  146. /* A wrapper for stat () which disallows pathnames that are empty strings. */
  147. static int
  148. test_stat (path, finfo)
  149.      char *path;
  150.      struct stat *finfo;
  151. {
  152.   if (*path == '\0')
  153.     {
  154.       errno = ENOENT;
  155.       return (-1);
  156.     }
  157.   return (stat (path, finfo));
  158. }
  159.  
  160. /* Do the same thing access(2) does, but use the effective uid and gid,
  161.    and don't make the mistake of telling root that any file is
  162.    executable. */
  163. static int
  164. eaccess (path, mode)
  165.      char *path;
  166.      int mode;
  167. {
  168.   extern int group_member ();
  169.   struct stat st;
  170.   static int euid = -1;
  171.  
  172.   if (test_stat (path, &st) < 0)
  173.     return (-1);
  174.  
  175.   if (euid == -1)
  176.     euid = geteuid ();
  177.  
  178.   if (euid == 0)
  179.     {
  180.       /* Root can read or write any file. */
  181.       if (mode != X_OK)
  182.     return (0);
  183.  
  184.       /* Root can execute any file that has any one of the execute
  185.      bits set. */
  186.       if (st.st_mode & S_IXUGO)
  187.     return (0);
  188.     }
  189.  
  190.   if (st.st_uid == euid)        /* owner */
  191.     mode <<= 6;
  192.   else if (group_member (st.st_gid))
  193.     mode <<= 3;
  194.  
  195.   if (st.st_mode & mode)
  196.     return (0);
  197.  
  198.   return (-1);
  199. }
  200.  
  201. #if defined (HAVE_GETGROUPS)
  202. /* The number of groups that this user is a member of. */
  203. static int ngroups = 0;
  204. static GETGROUPS_T *group_array = (GETGROUPS_T *)NULL;
  205. static int default_group_array_size = 0;
  206. #endif /* HAVE_GETGROUPS */
  207.  
  208. /* Return non-zero if GID is one that we have in our groups list. */
  209. int
  210. group_member (gid)
  211.      gid_t gid;
  212. {
  213. #if !defined (HAVE_GETGROUPS)
  214.   return ((gid == getgid ()) || (gid == getegid ()));
  215. #else
  216.   register int i;
  217.  
  218.   /* getgroups () returns the number of elements that it was able to
  219.      place into the array.  We simply continue to call getgroups ()
  220.      until the number of elements placed into the array is smaller than
  221.      the physical size of the array. */
  222.  
  223.   while (ngroups == default_group_array_size)
  224.     {
  225.       default_group_array_size += 64;
  226.  
  227.       group_array = (GETGROUPS_T *)
  228.     xrealloc (group_array,
  229.           default_group_array_size * sizeof (GETGROUPS_T));
  230.  
  231.       ngroups = getgroups (default_group_array_size, group_array);
  232.     }
  233.  
  234.   /* In case of error, the user loses. */
  235.   if (ngroups < 0)
  236.     return (0);
  237.  
  238.   /* Search through the list looking for GID. */
  239.   for (i = 0; i < ngroups; i++)
  240.     if (gid == group_array[i])
  241.       return (1);
  242.  
  243.   return (0);
  244. #endif /* HAVE_GETGROUPS */
  245. }
  246.  
  247. /* Increment our position in the argument list.  Check that we're not
  248.    past the end of the argument list.  This check is supressed if the
  249.    argument is FALSE.  Made a macro for efficiency. */
  250. #if !defined (lint)
  251. #define advance(f)    (++pos, f && (pos < argc ? 0 : beyond()))
  252. #endif
  253.  
  254. #if !defined (advance)
  255. static int
  256. advance (f)
  257.      int f;
  258. {
  259.   ++pos;
  260.  
  261.   if (f && pos >= argc)
  262.     beyond ();
  263. }
  264. #endif /* advance */
  265.  
  266. #define unary_advance() (advance (1),++pos)
  267.  
  268. /*
  269.  * beyond - call when we're beyond the end of the argument list (an
  270.  *    error condition)
  271.  */
  272. static int
  273. beyond ()
  274. {
  275.   test_syntax_error ("argument expected\n", (char *)NULL);
  276. }
  277.  
  278. /* Syntax error for when an integer argument was expected, but
  279.    something else was found. */
  280. static void
  281. integer_expected_error (pch)
  282.      char *pch;
  283. {
  284.   test_syntax_error ("integer expression expected %s\n", pch);
  285. }
  286.  
  287. /* Return non-zero if the characters pointed to by STRING constitute a
  288.    valid number.  Stuff the converted number into RESULT if RESULT is
  289.    a non-null pointer to a long. */
  290. static int
  291. isint (string, result)
  292.      register char *string;
  293.      long *result;
  294. {
  295.   int sign;
  296.   long value;
  297.  
  298.   sign = 1;
  299.   value = 0;
  300.  
  301.   if (result)
  302.     *result = 0;
  303.  
  304.   /* Skip leading whitespace characters. */
  305.   while (whitespace (*string))
  306.     string++;
  307.  
  308.   if (!*string)
  309.     return (0);
  310.  
  311.   /* We allow leading `-' or `+'. */
  312.   if (*string == '-' || *string == '+')
  313.     {
  314.       if (!digit (string[1]))
  315.     return (0);
  316.  
  317.       if (*string == '-')
  318.     sign = -1;
  319.  
  320.       string++;
  321.     }
  322.  
  323.   while (digit (*string))
  324.     {
  325.       if (result)
  326.     value = (value * 10) + digit_value (*string);
  327.       string++;
  328.     }
  329.  
  330.   /* Skip trailing whitespace, if any. */
  331.   while (whitespace (*string))
  332.     string++;
  333.  
  334.   /* Error if not at end of string. */
  335.   if (*string)
  336.     return (0);
  337.  
  338.   if (result)
  339.     {
  340.       value *= sign;
  341.       *result = value;
  342.     }
  343.  
  344.   return (1);
  345. }
  346.  
  347. /* Find the modification time of FILE, and stuff it into AGE, a pointer
  348.    to a long.  Return non-zero if successful, else zero. */
  349. static int
  350. age_of (filename, age)
  351.      char *filename;
  352.      long *age;
  353. {
  354.   struct stat finfo;
  355.  
  356.   if (test_stat (filename, &finfo) < 0)
  357.     return (0);
  358.  
  359.   if (age)
  360.     *age = finfo.st_mtime;
  361.  
  362.   return (1);
  363. }
  364.  
  365. /*
  366.  * term - parse a term and return 1 or 0 depending on whether the term
  367.  *    evaluates to true or false, respectively.
  368.  *
  369.  * term ::=
  370.  *    '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename
  371.  *    '-'('L'|'x') filename
  372.  *     '-t' [ int ]
  373.  *    '-'('z'|'n') string
  374.  *    string
  375.  *    string ('!='|'=') string
  376.  *    <int> '-'(eq|ne|le|lt|ge|gt) <int>
  377.  *    file '-'(nt|ot|ef) file
  378.  *    '(' <expr> ')'
  379.  * int ::=
  380.  *    '-l' string
  381.  *    positive and negative integers
  382.  */
  383. static int
  384. term ()
  385. {
  386.   int value;
  387.  
  388.   if (pos >= argc)
  389.     beyond ();
  390.  
  391.   /* Deal with leading "not"'s. */
  392.   if ('!' == argv[pos][0] && '\000' == argv[pos][1])
  393.     {
  394.       value = FALSE;
  395.       while (pos < argc && '!' == argv[pos][0] && '\000' == argv[pos][1])
  396.     {
  397.       advance (1);
  398.       value ^= (TRUE);
  399.     }
  400.  
  401.       return (value ^ (term ()));
  402.     }
  403.  
  404.   /* A paren-bracketed argument. */  
  405.   if (argv[pos][0] == '(' && !argv[pos][1])
  406.     {
  407.       advance (1);
  408.       value = expr ();
  409.       if (argv[pos][0] != ')' || argv[pos][1])
  410.     test_syntax_error ("')' expected, found %s\n", argv[pos]);
  411.       advance (0);
  412.       return (TRUE == (value));
  413.     }
  414.  
  415.   /* are there enough arguments left that this could be dyadic? */
  416.   if (((pos + 3 <= argc) && binop (argv[pos + 1])) ||
  417.       ((pos + 4 <= argc && STREQ (argv[pos], "-l") && binop (argv[pos + 2]))))
  418.     value = binary_operator ();
  419.  
  420.   /* Might be a switch type argument */
  421.   else if ('-' == argv[pos][0] && 0 == argv[pos][2])
  422.     {
  423.       if (unop (argv[pos][1]))
  424.     value = unary_operator ();
  425.       else
  426.     test_syntax_error ("%s: unary operator expected\n", argv[pos]);
  427.     }
  428.   else
  429.     {
  430.       value = (argv[pos][0] != '\0');
  431.       advance (0);
  432.     }
  433.  
  434.   return (value);
  435. }
  436.  
  437. static int
  438. binary_operator ()
  439. {
  440.   register int op;
  441.   struct stat stat_buf, stat_spare;
  442.   long int l, r, value;
  443.   /* Are the left and right integer expressions of the form '-l string'? */
  444.   int l_is_l, r_is_l;
  445.  
  446.   if (strcmp (argv[pos], "-l") == 0)
  447.     {
  448.       l_is_l = 1;
  449.       op = pos + 2;
  450.  
  451.       /* Make sure that OP is still a valid binary operator. */
  452.       if ((op >= argc - 1) || (binop (argv[op]) == 0))
  453.     test_syntax_error ("%s: binary operator expected\n", argv[op]);
  454.  
  455.       advance (0);
  456.     }
  457.   else
  458.     {
  459.       l_is_l = 0;
  460.       op = pos + 1;
  461.     }
  462.  
  463.   if ((op < argc - 2) && (strcmp (argv[op + 1], "-l") == 0))
  464.     {
  465.       r_is_l = 1;
  466.       advance (0);
  467.     }
  468.   else
  469.     r_is_l = 0;
  470.  
  471.   if (argv[op][0] == '-')
  472.     {
  473.       /* check for eq, nt, and stuff */
  474.       switch (argv[op][1])
  475.     {
  476.     default:
  477.       break;
  478.  
  479.     case 'l':
  480.       if (argv[op][2] == 't' && !argv[op][3])
  481.         {
  482.           /* lt */
  483.           if (l_is_l)
  484.         l = strlen (argv[op - 1]);
  485.           else
  486.         {
  487.           if (!isint (argv[op - 1], &l))
  488.             integer_expected_error ("before -lt");
  489.         }
  490.  
  491.           if (r_is_l)
  492.         r = strlen (argv[op + 2]);
  493.           else
  494.         {
  495.           if (!isint (argv[op + 1], &r))
  496.             integer_expected_error ("after -lt");
  497.         }
  498.           pos += 3;
  499.           return (TRUE == (l < r));
  500.         }
  501.  
  502.       if (argv[op][2] == 'e' && !argv[op][3])
  503.         {
  504.           /* le */
  505.           if (l_is_l)
  506.         l = strlen (argv[op - 1]);
  507.           else
  508.         {
  509.           if (!isint (argv[op - 1], &l))
  510.             integer_expected_error ("before -le");
  511.         }
  512.           if (r_is_l)
  513.         r = strlen (argv[op + 2]);
  514.           else
  515.         {
  516.           if (!isint (argv[op + 1], &r))
  517.             integer_expected_error ("after -le");
  518.         }
  519.           pos += 3;
  520.           return (TRUE == (l <= r));
  521.         }
  522.       break;
  523.  
  524.     case 'g':
  525.       if (argv[op][2] == 't' && !argv[op][3])
  526.         {
  527.           /* gt integer greater than */
  528.           if (l_is_l)
  529.         l = strlen (argv[op - 1]);
  530.           else
  531.         {
  532.           if (!isint (argv[op - 1], &l))
  533.             integer_expected_error ("before -gt");
  534.         }
  535.           if (r_is_l)
  536.         r = strlen (argv[op + 2]);
  537.           else
  538.         {
  539.           if (!isint (argv[op + 1], &r))
  540.             integer_expected_error ("after -gt");
  541.         }
  542.           pos += 3;
  543.           return (TRUE == (l > r));
  544.         }
  545.  
  546.       if (argv[op][2] == 'e' && !argv[op][3])
  547.         {
  548.           /* ge - integer greater than or equal to */
  549.           if (l_is_l)
  550.         l = strlen (argv[op - 1]);
  551.           else
  552.         {
  553.           if (!isint (argv[op - 1], &l))
  554.             integer_expected_error ("before -ge");
  555.         }
  556.           if (r_is_l)
  557.         r = strlen (argv[op + 2]);
  558.           else
  559.         {
  560.           if (!isint (argv[op + 1], &r))
  561.             integer_expected_error ("after -ge");
  562.         }
  563.           pos += 3;
  564.           return (TRUE == (l >= r));
  565.         }
  566.       break;
  567.  
  568.     case 'n':
  569.       if (argv[op][2] == 't' && !argv[op][3])
  570.         {
  571.           /* nt - newer than */
  572.           pos += 3;
  573.           if (l_is_l || r_is_l)
  574.         test_syntax_error ("-nt does not accept -l\n", (char *)NULL);
  575.           if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
  576.         return (TRUE == (l > r));
  577.           else
  578.         return (FALSE);
  579.         }
  580.  
  581.       if (argv[op][2] == 'e' && !argv[op][3])
  582.         {
  583.           /* ne - integer not equal */
  584.           if (l_is_l)
  585.         l = strlen (argv[op - 1]);
  586.           else
  587.         {
  588.           if (!isint (argv[op - 1], &l))
  589.             integer_expected_error ("before -ne");
  590.         }
  591.           if (r_is_l)
  592.         r = strlen (argv[op + 2]);
  593.           else
  594.         {
  595.           if (!isint (argv[op + 1], &r))
  596.             integer_expected_error ("after -ne");
  597.         }
  598.           pos += 3;
  599.           return (TRUE == (l != r));
  600.         }
  601.       break;
  602.  
  603.     case 'e':
  604.       if (argv[op][2] == 'q' && !argv[op][3])
  605.         {
  606.           /* eq - integer equal */
  607.           if (l_is_l)
  608.         l = strlen (argv[op - 1]);
  609.           else
  610.         {
  611.           if (!isint (argv[op - 1], &l))
  612.             integer_expected_error ("before -eq");
  613.         }
  614.           if (r_is_l)
  615.         r = strlen (argv[op + 2]);
  616.           else
  617.         {
  618.           if (!isint (argv[op + 1], &r))
  619.             integer_expected_error ("after -eq");
  620.         }
  621.           pos += 3;
  622.           return (TRUE == (l == r));
  623.         }
  624.  
  625.       if (argv[op][2] == 'f' && !argv[op][3])
  626.         {
  627.           /* ef - hard link? */
  628.           pos += 3;
  629.           if (l_is_l || r_is_l)
  630.         test_syntax_error ("-ef does not accept -l\n", (char *)NULL);
  631.           if (stat (argv[op - 1], &stat_buf) < 0)
  632.         return (FALSE);
  633.           if (stat (argv[op + 1], &stat_spare) < 0)
  634.         return (FALSE);
  635.           return (TRUE ==
  636.               (stat_buf.st_dev == stat_spare.st_dev &&
  637.                stat_buf.st_ino == stat_spare.st_ino));
  638.         }
  639.       break;
  640.  
  641.     case 'o':
  642.       if ('t' == argv[op][2] && '\000' == argv[op][3])
  643.         {
  644.           /* ot - older than */
  645.           pos += 3;
  646.           if (l_is_l || r_is_l)
  647.         test_syntax_error ("-nt does not accept -l\n", (char *)NULL);
  648.           if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
  649.         return (TRUE == (l < r));
  650.           return (FALSE);
  651.         }
  652.       break;
  653.     }
  654.       test_syntax_error ("unknown binary operator", argv[op]);
  655.     }
  656.  
  657.   if (argv[op][0] == '=' && !argv[op][1])
  658.     {
  659.       value = (strcmp (argv[pos], argv[pos + 2]) == 0);
  660.       pos += 3;
  661.       return (TRUE == value);
  662.     }
  663.  
  664.   if (strcmp (argv[op], "!=") == 0)
  665.     {
  666.       value = (strcmp (argv[pos], argv[pos + 2]) != 0);
  667.       pos += 3;
  668.       return (TRUE == value);
  669.     }
  670. }
  671.  
  672. static int
  673. unary_operator ()
  674. {
  675.   long r, value;
  676.   struct stat stat_buf;
  677.  
  678.   switch (argv[pos][1])
  679.     {
  680.     default:
  681.       return (FALSE);
  682.  
  683.       /* All of the following unary operators use unary_advance (), which
  684.      checks to make sure that there is an argument, and then advances
  685.      pos right past it.  This means that pos - 1 is the location of the
  686.      argument. */
  687.  
  688.     case 'a':            /* file exists in the file system? */
  689.     case 'e':
  690.       unary_advance ();
  691.       value = -1 != test_stat (argv[pos - 1], &stat_buf);
  692.       return (TRUE == value);
  693.  
  694.     case 'r':            /* file is readable? */
  695.       unary_advance ();
  696.       value = -1 != eaccess (argv[pos - 1], R_OK);
  697.       return (TRUE == value);
  698.  
  699.     case 'w':            /* File is writeable? */
  700.       unary_advance ();
  701.       value = -1 != eaccess (argv[pos - 1], W_OK);
  702.       return (TRUE == value);
  703.  
  704.     case 'x':            /* File is executable? */
  705.       unary_advance ();
  706.       value = -1 != eaccess (argv[pos - 1], X_OK);
  707.       return (TRUE == value);
  708.  
  709.     case 'O':            /* File is owned by you? */
  710.       unary_advance ();
  711.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  712.     return (FALSE);
  713.  
  714.       return (TRUE == (geteuid () == stat_buf.st_uid));
  715.  
  716.     case 'G':            /* File is owned by your group? */
  717.       unary_advance ();
  718.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  719.     return (FALSE);
  720.  
  721.       return (TRUE == (getegid () == stat_buf.st_gid));
  722.  
  723.     case 'f':            /* File is a file? */
  724.       unary_advance ();
  725.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  726.     return (FALSE);
  727.  
  728.       /* Under POSIX, -f is true if the given file exists
  729.      and is a regular file. */
  730.       return (TRUE == ((S_ISREG (stat_buf.st_mode)) ||
  731.                (0 == (stat_buf.st_mode & S_IFMT))));
  732.  
  733.     case 'd':            /* File is a directory? */
  734.       unary_advance ();
  735.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  736.     return (FALSE);
  737.  
  738.       return (TRUE == (S_ISDIR (stat_buf.st_mode)));
  739.  
  740.     case 's':            /* File has something in it? */
  741.       unary_advance ();
  742.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  743.     return (FALSE);
  744.  
  745.       return (TRUE == (stat_buf.st_size > (off_t) 0));
  746.  
  747.     case 'S':            /* File is a socket? */
  748. #if !defined (S_ISSOCK)
  749.       return (FALSE);
  750. #else
  751.       unary_advance ();
  752.  
  753.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  754.     return (FALSE);
  755.  
  756.       return (TRUE == (S_ISSOCK (stat_buf.st_mode)));
  757. #endif                /* S_ISSOCK */
  758.  
  759.     case 'c':            /* File is character special? */
  760.       unary_advance ();
  761.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  762.     return (FALSE);
  763.  
  764.       return (TRUE == (S_ISCHR (stat_buf.st_mode)));
  765.  
  766.     case 'b':            /* File is block special? */
  767.       unary_advance ();
  768.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  769.     return (FALSE);
  770.  
  771.       return (TRUE == (S_ISBLK (stat_buf.st_mode)));
  772.  
  773.     case 'p':            /* File is a named pipe? */
  774.       unary_advance ();
  775. #ifndef S_ISFIFO
  776.       return (FALSE);
  777. #else
  778.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  779.     return (FALSE);
  780.       return (TRUE == (S_ISFIFO (stat_buf.st_mode)));
  781. #endif                /* S_ISFIFO */
  782.  
  783.     case 'L':            /* Same as -h  */
  784.       /*FALLTHROUGH*/
  785.  
  786.     case 'h':            /* File is a symbolic link? */
  787.       unary_advance ();
  788. #ifndef S_ISLNK
  789.       return (FALSE);
  790. #else
  791.       /* An empty filename is not a valid pathname. */
  792.       if ((argv[pos - 1][0] == '\0') ||
  793.       (lstat (argv[pos - 1], &stat_buf) < 0))
  794.     return (FALSE);
  795.  
  796.       return (TRUE == (S_ISLNK (stat_buf.st_mode)));
  797. #endif                /* S_IFLNK */
  798.  
  799.     case 'u':            /* File is setuid? */
  800.       unary_advance ();
  801.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  802.     return (FALSE);
  803.  
  804.       return (TRUE == (0 != (stat_buf.st_mode & S_ISUID)));
  805.  
  806.     case 'g':            /* File is setgid? */
  807.       unary_advance ();
  808.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  809.     return (FALSE);
  810.  
  811.       return (TRUE == (0 != (stat_buf.st_mode & S_ISGID)));
  812.  
  813.     case 'k':            /* File has sticky bit set? */
  814.       unary_advance ();
  815.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  816.     return (FALSE);
  817. #if !defined (S_ISVTX)
  818.       /* This is not Posix, and is not defined on some Posix systems. */
  819.       return (FALSE);
  820. #else
  821.       return (TRUE == (0 != (stat_buf.st_mode & S_ISVTX)));
  822. #endif
  823.  
  824.     case 't':            /* File (fd) is a terminal?  (fd) defaults to stdout. */
  825.       advance (0);
  826.       if (pos < argc && isint (argv[pos], &r))
  827.     {
  828.       advance (0);
  829.       return (TRUE == (isatty ((int) r)));
  830.     }
  831.       return (TRUE == (isatty (1)));
  832.  
  833.     case 'n':            /* True if arg has some length. */
  834.       unary_advance ();
  835.       return (TRUE == (argv[pos - 1][0] != 0));
  836.  
  837.     case 'z':            /* True if arg has no length. */
  838.       unary_advance ();
  839.       return (TRUE == (argv[pos - 1][0] == '\0'));
  840.     }
  841. }
  842.     
  843. /*
  844.  * and:
  845.  *    term
  846.  *    term '-a' and
  847.  */
  848. static int
  849. and ()
  850. {
  851.   int value;
  852.  
  853.   value = term ();
  854.   while ((pos < argc) && strcmp (argv[pos], "-a") == 0)
  855.     {
  856.       advance (0);
  857.       value = TRUTH_AND (value, and ());
  858.     }
  859.   return (TRUE == value);
  860. }
  861.  
  862. /*
  863.  * or:
  864.  *    and
  865.  *    and '-o' or
  866.  */
  867. static int
  868. or ()
  869. {
  870.   int value;
  871.  
  872.   value = and ();
  873.  
  874.   while ((pos < argc) && strcmp (argv[pos], "-o") == 0)
  875.     {
  876.       advance (0);
  877.       value = TRUTH_OR (value, or ());
  878.     }
  879.  
  880.   return (TRUE == value);
  881. }
  882.  
  883. /*
  884.  * expr:
  885.  *    or
  886.  */
  887. static int
  888. expr ()
  889. {
  890.   if (pos >= argc)
  891.     beyond ();
  892.  
  893.   return (FALSE ^ (or ()));        /* Same with this. */
  894. }
  895.  
  896. /* Return TRUE if S is one of the test command's binary operators. */
  897. static int
  898. binop (s)
  899.      char *s;
  900. {
  901.   return ((STREQ (s,   "=")) || (STREQ (s,  "!=")) || (STREQ (s, "-nt")) ||
  902.       (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
  903.       (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
  904.       (STREQ (s, "-gt")) || (STREQ (s, "-ge")));
  905. }
  906.  
  907. /* Return non-zero if OP is one of the test command's unary operators. */
  908. static int
  909. unop (op)
  910.      int op;
  911. {
  912.   return (member (op, "abcdefgkLhprsStuwxOGnz"));
  913. }
  914.  
  915. static int
  916. two_arguments ()
  917. {
  918.   int value;
  919.  
  920.   if (STREQ (argv[pos], "!"))
  921.     value = strlen (argv[pos+1]) == 0;
  922.   else if ((argv[pos][0] == '-') && (argv[pos][2] == '\0'))
  923.     {
  924.       if (unop (argv[pos][1]))
  925.     value = unary_operator ();
  926.       else
  927.     test_syntax_error ("%s: unary operator expected\n", argv[pos]);
  928.     }
  929.   else
  930.     beyond ();
  931.   return (value);
  932. }
  933.  
  934. static int
  935. three_arguments ()
  936. {
  937.   int value;
  938.  
  939.   if (STREQ (argv[pos], "!"))
  940.     {
  941.       advance (1);
  942.       value = !two_arguments ();
  943.     }
  944.   else if (binop (argv[pos+1]))
  945.     {
  946.       value = binary_operator ();
  947.       pos = argc;
  948.     }
  949.   else if ((STREQ (argv[pos+1], "-a")) || (STREQ (argv[pos+1], "-o")) ||
  950.        (argv[pos][0] == '('))
  951.     value = expr ();
  952.   else
  953.     test_syntax_error ("%s: binary operator expected\n", argv[pos+1]);
  954.   return (value);
  955. }
  956.  
  957. /* This is an implementation of a Posix.2 proposal by David Korn. */
  958. static int
  959. posixtest ()
  960. {
  961.   int value;
  962.  
  963.   switch (argc - 1)    /* one extra passed in */
  964.     {
  965.       case 0:
  966.     value = FALSE;
  967.     pos = argc;
  968.     break;
  969.  
  970.       case 1:
  971.     value = strlen (argv[1]) != 0;
  972.     pos = argc;
  973.     break;
  974.  
  975.       case 2:
  976.     value = two_arguments ();
  977.     pos = argc;
  978.     break;
  979.  
  980.       case 3:
  981.     value = three_arguments ();
  982.     break;
  983.  
  984.       case 4:
  985.     if (STREQ (argv[pos], "!"))
  986.       {
  987.         advance (1);
  988.         value = !three_arguments ();
  989.         break;
  990.       }
  991.     /* FALLTHROUGH */
  992.       case 5:
  993.       default:
  994.     value = expr ();
  995.     }
  996.  
  997.   return (value);
  998. }
  999.  
  1000. /*
  1001.  * [:
  1002.  *    '[' expr ']'
  1003.  * test:
  1004.  *    test expr
  1005.  */
  1006. int
  1007. #if defined (TEST_STANDALONE)
  1008. main (margc, margv)
  1009. #else
  1010. test_command (margc, margv)
  1011. #endif /* !TEST_STANDALONE */
  1012.      int margc;
  1013.      char **margv;
  1014. {
  1015.   auto int value;
  1016.   int expr ();
  1017.  
  1018. #if !defined (TEST_STANDALONE)
  1019.   int code;
  1020.  
  1021.   code = setjmp (test_exit_buf);
  1022.  
  1023.   if (code)
  1024.     return (test_error_return);
  1025. #else /* TEST_STANDALONE */
  1026.   program_name = margv[0];
  1027. #endif /* TEST_STANDALONE */
  1028.  
  1029.   argv = margv;
  1030.  
  1031.   if (margv[0] && strcmp (margv[0], "[") == 0)
  1032.     {
  1033.       --margc;
  1034.  
  1035.       if (margc < 2)
  1036.     test_exit (SHELL_BOOLEAN (FALSE));
  1037.  
  1038.       if (margv[margc] && strcmp (margv[margc], "]") != 0)
  1039.     test_syntax_error ("missing `]'\n", (char *)NULL);
  1040.     }
  1041.  
  1042.   argc = margc;
  1043.   pos = 1;
  1044.  
  1045.   if (pos >= argc)
  1046.     test_exit (SHELL_BOOLEAN (FALSE));
  1047.  
  1048.   value = posixtest ();
  1049.  
  1050.   if (pos != argc)
  1051.     test_syntax_error ("too many arguments\n", (char *)NULL);
  1052.  
  1053.   test_exit (SHELL_BOOLEAN (value));
  1054. }
  1055.